/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.sarco.bwi.lib;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 *
 * @author iktsup
 */
public class Blender {
    private static String PARAM_BG           = "-b";
    private static String PARAM_START_FRAME  = "-s";
    private static String PARAM_END_FRAME    = "-e";
    private static String PARAM_PATH         = "-o";
    private static String PARAM_FORMAT       = "-F";
    private static String PARAM_ADD_FILE_EXT = "-x";
    private static String PARAM_ANIMATION    = "-a";
    private static String PARAM_DEBUG        = "-d";

    private static String MSG_APPEND_FRAME   = "Append frame ";
    private static String MSG_TIME           = "Time: ";

    private static String SPACE = " ";

    /**
     * The default path to render the animation too.
     */
    public static String DEFAULT_PATH = "//render_#";

    private String blenderPath;
    private String file;
    private int startFrame;
    private int endFrame;
    private String path;
    private String format;
    private int addFileExt;
    private Socket client;

    private boolean hasSocket = false;

    private String command;

    /**
     * Creates a new Blender instance
     * @param blenderPath The path to the blender executeable
     * @param file Which .blend file to render
     * @param startFrame Starting frame to render
     * @param endFrame Last frame to render
     * @param path Path to the rendered file(s),
     * it follows the blender arguments syntax, see blender's manual for details.
     * @param format Which file format to render to. It follows the blender
     * arguments syntax, see blender's manual for details.
     * @param addFileExt 0 or 1, if to automatically add the file extension to
     * the rendered file(s).
     * @param client
     */
    public Blender(String blenderPath, String file, int startFrame,
            int endFrame, String path, String format, int addFileExt,
            Socket client) {

        System.out.println("Starting blender init!");

        this.blenderPath = blenderPath;
        this.file = file;
        this.startFrame = startFrame;
        this.endFrame = endFrame;
        this.path = path;
        this.format = format;
        this.addFileExt = addFileExt;

        this.command = this.blenderPath
                 +Blender.SPACE
                 +Blender.PARAM_BG+Blender.SPACE+this.file
                 +Blender.SPACE
                 +Blender.PARAM_PATH+Blender.SPACE+this.path
                 +Blender.SPACE
                 +Blender.PARAM_FORMAT+Blender.SPACE+this.format
                 +Blender.SPACE
                 +Blender.PARAM_ADD_FILE_EXT+Blender.SPACE+this.addFileExt
                 +Blender.SPACE
                 +Blender.PARAM_START_FRAME+Blender.SPACE+this.startFrame
                 +Blender.SPACE
                 +Blender.PARAM_END_FRAME+Blender.SPACE+this.endFrame
                 +Blender.SPACE
                 +Blender.PARAM_ANIMATION;

        this.client = client;

        if (this.client != null)
            this.hasSocket = true;

        System.out.println("Ending blender init!");
    }

    /**
     * Sets the path to where the Blender executeable
     * is located.
     * @param blenderPath Path the the Blender executeable
     */
    public void setBlenderPath(String blenderPath) {
        this.blenderPath = blenderPath;
    }

    /**
     *
     * @return Gets the path to where the Blender executeable is located.
     */
    public String getBlenderPath() {
        return this.blenderPath;
    }

    /**
     * Starts the render job
     */
    public void render() {
        boolean validCommand = true;
        System.out.println("Starting render!");

        if (this.getBlenderPath() == null)
            validCommand = false;
        if (this.getEndFrame() < this.getStartFrame()) {
            this.setEndFrame(getStartFrame());
            System.out.println(
                    "WARNING! EndFrame is less than StartFrame!");
            System.out.println(
                    "WARNING! Setting EndFrame to equal of StartFrame");
        }
        if (this.getFile() == null)
            validCommand = false;
        if (this.getFormat() == null)
            validCommand = false;
        if (this.getPath() == null) {
            this.setPath(Blender.DEFAULT_PATH);
            System.out.println("WARNING! No output set!");
            System.out.println("WARNING! Setting output to \""
                    + Blender.DEFAULT_PATH +"\"");
        }

        if (validCommand) {
            try {
                Runtime run = Runtime.getRuntime();
                Process pr = run.exec(command);
                BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream()));
                String line = buf.readLine();
                while (line != null) {
                    if (line.startsWith(Blender.MSG_APPEND_FRAME)) {
                        System.out.println(line);

                        int frame = 0;
                        {
                            String s = line.substring(Blender.MSG_APPEND_FRAME.length());
                            s = s.substring(0, s.indexOf(Blender.MSG_TIME));
                            s = s.replaceAll(" ", "");
                            frame = Integer.parseInt(s);
                        }

                        String sTime = line.substring(Blender.MSG_APPEND_FRAME.length() + 2);
                        sTime = sTime.substring(Blender.MSG_TIME.length());

                        getTimeLeft(frame, sTime);
                    }
                    line = buf.readLine();
                }

                pr.destroy();

                byte[] bOut = new byte[128];
                bOut = Node.NODE_DONE.getBytes();
                if (this.hasSocket)
                    this.client.getOutputStream().write(bOut);
            } catch (IOException ex) {
                
            }
        }
    }

    /**
     *
     * @param currFrame
     * @param sTime
     */
    public void getTimeLeft(int currFrame, String sTime) {
        try {
            double hour = 0;
            double min = 0;
            double sec = 0;
            String[] times = sTime.split(":");
            if (!times[0].isEmpty()) {
                min = Double.parseDouble(times[0]);
            }
            if (!times[1].isEmpty()) {
                sec = Double.parseDouble(times[1].substring(0, 2));
            }
            int lMin = 0;
            int lSec = 0;
            double s = (sec * (this.getEndFrame() - currFrame));
            double m = s / 60;
            if (m < 0.) {
                lMin = 0;
            } else {
                lMin = (int) m;
            }
            if (s < 60)
                lSec = (int) s;
            else
                lSec = (int) ( s % 60);
            
            String sTimeLeft = "Time left: " + lMin + "m:" + lSec + "s";
            System.out.println(sTimeLeft);
            double dPercentDone = 0.0;
            // Percent calculation:
            {
                int S = this.getStartFrame();
                int E = this.getEndFrame();
                int D = E - S;
                int C = currFrame;
                double P = (double) E - (double) C;
                P /= (double) D;
                P *= 100;
                dPercentDone = 100 - P;
            }
            String sPercentDone = String.valueOf(dPercentDone);
            sPercentDone += "00";
            sPercentDone = sPercentDone.substring(0, sPercentDone.indexOf(".") + 3);
            String sOut = Node.NODE_STATUS + currFrame + Node.COMMON_SPLIT + sPercentDone + Node.COMMON_SPLIT + sTimeLeft;

            if (this.hasSocket)
                this.client.getOutputStream().write(sOut.getBytes());

            sPercentDone += "% Done!";
            System.out.println(sPercentDone);
        } catch (IOException ex) {
            
        }
    }

    /**
     * @return the file
     */
    public String getFile() {
        return file;
    }

    /**
     * @param file the file to set
     */
    public void setFile(String file) {
        this.file = file;
    }

    /**
     * @return the startFrame
     */
    public int getStartFrame() {
        return startFrame;
    }

    /**
     * @param startFrame the startFrame to set
     */
    public void setStartFrame(int startFrame) {
        this.startFrame = startFrame;
    }

    /**
     * @return the endFrame
     */
    public int getEndFrame() {
        return endFrame;
    }

    /**
     * @param endFrame the endFrame to set
     */
    public void setEndFrame(int endFrame) {
        this.endFrame = endFrame;
    }

    /**
     * @return the path
     */
    public String getPath() {
        return path;
    }

    /**
     * @param path the path to set
     */
    public void setPath(String path) {
        this.path = path;
    }

    /**
     * @return the format
     */
    public String getFormat() {
        return format;
    }

    /**
     * @param format the format to set
     */
    public void setFormat(String format) {
        this.format = format;
    }

    /**
     * @return the addFileExt
     */
    public int getAddFileExt() {
        return addFileExt;
    }

    /**
     * @param addFileExt the addFileExt to set
     */
    public void setAddFileExt(int addFileExt) {
        this.addFileExt = addFileExt;
    }
}
